/*

  Copyright (C) 2010  INRIA, Planète Team

  Authors:
  ------------------------------------------------------------
   Amir Krifa                        :  krifa.amir@gmail.com
   Chadi Barakat                     :  Chadi.Barakat@sophia.inria.fr
  ------------------------------------------------------------

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 3
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#ifndef LINK_H
#define LINK_H

#include <string>
#include <pthread.h>
#include <queue>
#include <semaphore.h>
#include <pthread.h>


class Node;
class XMLTree;
class Link;
class Links;

typedef struct threadParam
{
	Link * currentLink;
	threadParam()
	{
		currentLink = NULL;
	}
}threadParam;

class LinkMessage 
{
public:	
	const static int MSG_WAKEUP    = 0;
	const static int MSG_STATE     = 1;
	const static int MSG_BUNDLE    = 2;
	const static int MSG_TRANSMIT  = 3;
	const static int MSG_ATTACH    = 4;
	int type;
	LinkMessage() 
	{
		type = MSG_WAKEUP;
	}
	LinkMessage(int what) 
	{
		type = what;
	}
};

class Link
{
public:	
	// Link types
	const static int TYPE_ALWAYSON      = 0;
	const static int TYPE_ONDEMAND      = 1;
	const static int TYPE_SCHEDULED     = 2;
	const static int TYPE_OPPORTUNISTIC = 3;
	const static int TYPE_OTHER         = 4;
	
	// Link states. Do not assume the link goes from one state to the next.
	// But the ordinal values below are important.
	const static int STATE_NONEXTANT    = 0;
	const static int STATE_UNAVAILABLE  = 1;
	const static int STATE_AVAILABLE    = 2;
	const static int STATE_CLOSED       = 3;
	const static int STATE_OPENING      = 4;
	const static int STATE_OPEN         = 5;
	const static int STATE_BUSY         = 6;
	const static int DEFAULT_MAX_OUTSTANDING_SENDS = 10;
	
	
	/**
	 * Constructor: Create queues for receiving control messages between
	 * the main and Link thread.
	 * 
	 * @param Instance of the Links class creating this link.
	 */
	Link(Links *links);
	
	~Link();
	///////////////////////////// Link Thread //////////////////////////////
	
	
	/**
	 * Body of thread. Loop waiting for a message to arrive on
	 * the queue. Stop when the action variable changes to ACTION_STOP.
	 */
	static void* run(void *);
	
	/**
	 * Called to notify the link thread that a bundle has been queued to
	 * the active node.
	 */
	void bundleNotify() 
	{
		sem_wait(&queueSem);
		msgQueue.push(new LinkMessage(LinkMessage::MSG_BUNDLE));
		sem_post(&queueSem);
	}
	
	/**
	 * Called when the link becomes available. Start the thread
	 * if not running. If the link had been busy then this probably
	 * means that it is open as opposed to able to be opened. If our
	 * current state is open then we just ignore the request because
	 * it probably means the link had been busy and no longer is busy
	 * (and for some reason we didn't realize that it was busy). Got
	 * that?
	 * 
	 * element Root XMLTree element for the event. 
	 */
	void available(XMLTree *element);
	
	/**
	 * Called when the link becomes unavailable.
	 * 
	 * @param element Root XMLTree element for the event.
	 */
	void unavailable(XMLTree *element) 
	{
		openRequested = false;
		stateChange(STATE_UNAVAILABLE);
	}
	
	/**
	 * Called when the link is opened. Start thread if it's not
	 * running. If the state was busy then we treat this as "still
	 * open and no longer busy." (But in practice all that we have seen
	 * is an available event after a busy event if the link is still
	 * open.)
	 * 
	 * @param element Root XMLTree element for the event.
	 */
	void opened(XMLTree *element);
	
	/**
	 * Called when the link is closed.
	 * 
	 * @param element Root XMLTree element for the event.
	 */
	void closed(XMLTree *element);

	/**
	 * Called when the link is noted as busy. We only allow the link
	 * to become busy if it had been open since we treat busy as a
	 * flow-control of an open link.
	 * 
	 * @param element Root XMLTree element for the event.
	 */
	void busy(XMLTree *element);
	
	/**
	 * Called when data sent by this link has been transmitted. The policy
	 * manager does most of the work. When we're done calling the policy
	 * manager we wake up the link thread since it may be flow controlled
	 * on the number of bundles outstanding.
	 * 
	 * @param id Local id of the bundle.
	 * @param bytesSent Total bytes sent.
	 * @param bytesReliable Bytes reliably sent.
	 */
	void dataTransmitted(long id, int bytesSent, int bytesReliable);

	/**
	 * Called to determine if this link is open and associated with a Node
	 * object.
	 * 
	 * @return True if associated and open, else false.
	 */
	bool isAssociated() 
	{
		if ((state < STATE_OPEN) || (currentNode == NULL)) 
		{
			return false;
		}
		return true;
	}
	
	/**
	 * Called to do the association of a Node to this link if this link is
	 * currently open. This is typically done when two links had been open
	 * to the same node, but the node had been active on the other link.
	 * Presumably, the other link is now closed while this link remains open.
	 * 
	 * @param node Node to be associated.
	 * @param switching If true, then this method is being called by the
	 *    link that is closing, otherwise this method is being called by the
	 *    node issuing a conditional open.
	 * @return True if an association was made, else false.
	 */
	bool associate(Node *node, bool switching);
	
	/**
	 * Called to request that a Link associate with a particular Node.
	 * If the link is already open and in contact with a different EID
	 * then the request is ignored. If the link is in contact with the EID
	 * but not already associated with the Node object that represents
	 * the EID, then make the association. Otherwise, if this is a link
	 * that we can request to have opened, request that the link be
	 * opened. If and when the link is opened, an association to the Node
	 * object will be made. 
	 * 
	 * @param node Node object and likely caller. There should be a
	 *    route between this link and the node, either persistent or
	 *    temporal. 
	 */
	void conditionalUse(Node* node);
	
	/**
	 * Requests that the link be opened. This method is typically used
	 * by the policy manager to open a link.  
	 */
	void requestOpen();
	
	/**
	 * Called when a "contact_attribute_changed_event" message is received.
	 * Stores away the new contact attributes. Also stores new link and
	 * clinfo attributes if they also exist.
	 * 
	 * @param evtContactAttrsChanged Root contact_attribute_changed_event
	 *    element.
	 */
	void contactAttrsChanged(XMLTree *evtContactAttrsChanged) 
	{
		findAndSaveAttributesContact(evtContactAttrsChanged);
	}
	
	/**
	 * Called when a "link_attribute_changed_event" message is received.
	 * Stores away the new link attributes. Also stores new clinfo
	 * attributes if they exist.
	 * 
	 * @param evtContactAttrsChanged Root link_attribute_changed_event
	 *    element.
	 */
	void linkAttrsChanged(XMLTree* evtLinkAttrsChanged) 
	{
		findAndSaveAttributesLink(evtLinkAttrsChanged);
	}
	
	
	/**
	 * Returns the stored contact_attr element.
	 * 
	 * @return XML element.
	 */
	XMLTree *getContactElement() 
	{
		return contactAttrs;
	}
	
	
	/**
	 * Returns the defined remote address for the link.
	 * 
	 * @return Remote address, as obtained from the <clinfo> element.
	 */

	std::string getRemoteAddr() 
	{
		return remoteAddr;
	}
	
	/**
	 * Returns the stored link_attr element.
	 * 
	 * @return XML element.
	 */

	XMLTree *getLinkElement() 
	{
		return linkAttrs;
	}
	
	
	/**
	 * Returns the stored clinfo element.
	 * 
	 * @return XML element.
	 */
	XMLTree *getCLinfoElement() 
	{
		return clInfo;
	}
	/**
	 * Initialize a Link given an event. The creation and initialization of
	 * a link is normally done via the create() method of the Links class.
	 * 
	 * @param evtLinkCreated link_created_event as the root of a tree.
	 * return True if initialized ok, else false.
	 */
	bool init(XMLTree *evtLinkCreated);

	/**
	 * Called when the link is deleted. This should not be called
	 * directly, but instead be called by Links.delete(). If the link
	 * thread is running set the action variable to tell the thread
	 * to stop. When called, the Links class should have no more
	 * knowledge of this instance of the link.
	 * 
	 * @param element Root XMLTree element for the event.
	 */
	void deleted(XMLTree *element) 
	{
		if (thread == 0) 
		{
			return;
		}
		openRequested = false;
		action = ACTION_STOP;
		stateChange(STATE_NONEXTANT);
	}

	std::string id;
	std::string remoteEID;
	
protected:
	
	
	
	
	int type;
	int state;
	int maxSendsOutstanding; 
	// Set once and not changed: the link's id, i.e. name.
	
	int linkMaxSendsOutstanding;
	
	LinkMessage* msgQueueTake()
	{
		LinkMessage* tmp = NULL;
		sem_wait(&queueSem);
		tmp = msgQueue.front();
		msgQueue.pop();
		sem_post(&queueSem);
		return tmp;			
	}
	
private:

	/**
	 * Gets pertinent information from the link_attr element.
	 * 
	 * @param element Element as represented by the XMLTree class.
	 * @return True if required attributes exist, else false.
	 */
	bool parseLinkAttr(XMLTree* element);
	
	/**
	 * Do any clean-up before terminating the thread. 
	 */
	void finish() 	
	{
		while(!msgQueue.empty())
		{
			delete msgQueue.front();
			msgQueue.pop();
		}
	}
	
	/**
	 * Called when the state of the link changes. A message is queued to
	 * wake up the thread. Note that subsequent events may cause the state
	 * to change such that when the thread gets this message the state may 
	 * be different from what it is now. The message is merely an indication
	 * that the thread should check to see if its state has changed. But,
	 * state changes to/from the OPEN state are synchronized.
	 * 
	 * Some notes on states. We only permit going to the busy state from the
	 * open state. We treat the available state as not open, though DTN will
	 * send us an "available event" when the link is no longer busy. In other
	 * words, a DTN available link really can be either open or not open. We
	 * handle an available event after a busy event by changing the link
	 * state back to open.
	 * 
	 * Some side effects of this method:
	 *    - If there is a node associated with the link and we're going
	 *      from an open to non-open state then un-associate the node and
	 *      link.
	 *    - If it's a transition between open and non-open (either direction)
	 *      then either create or delete a temporal route.
	 *    - If a transition from an open state to a non-open state and there
	 *      is a node associated with the link, determine if there is another
	 *      open link that can be used by the node.
	 * 
	 * @param newState State changing to. Note that writing this value to the
	 *    state variable is defined by Java as an atomic action.
	 */
	void stateChange(int newState);
	/**
	 * Converts a string type value to an integer value.
	 * 
	 * @param str string value.
	 * @return Integer value.
	 */
	int whatType(std::string str);
	
	/**
	 * Once awake, this is the link thread's primary loop. Look for changes in
	 * state, and send bundles if the conditions are appropriate.
	 */
	void takeAction();
	
	/**
	 * Send a bundle. Notify the policy manager if there was a bundle
	 * that we requested be sent.
	 * 
	 * @param queue Queue to get the bundle from.
	 * @return True if there was data to be sent.
	 */
	bool sendBundle(int queue);
		
	/**
	 * Checks to see if the state of the link has changed since
	 * the last time we looked. We only synchronize state changes
	 * from/to the OPEN state with the main thread.
	 * 
	 * A note on the link state: When the main thread receives
	 * notification of a change in a link's state it sets the variable
	 * state and wakes up the link's thread. The link's thread then
	 * sets its state  (thrdState) to the value of state. There are
	 * a couple side effects of this:
	 *    1) state and thrdState may be inconsistent;
	 *    2) the "state" variable can potentially change multiple
	 *       times before thrdState becomes consistent.
	 * But that's OK if you remember that this router is built on
	 * an asynchronous interface and so "state" may be inconsistent
	 * with the actual state of the link as viewed by DTN.
	 * 
	 * However we do synchronize on a link transition to/from the
	 * OPEN state. When a link becomes OPEN then we need to make
	 * copies of some values pertaining to the link before acknowledging
	 * the open.
	 * 
	 * @return True if the state changed, else false.
	 */
	bool checkState();
	
	/**
	 * Called when the link is open but no node was associated with the
	 * link by the router, yet the router now indicates a node object is
	 * ready to be associated. Do the association and synch up with the
	 * main thread. 
	 */
	void attachToNode();
	
	///////////////////////////// Main Thread //////////////////////////////
			
	/**
	 * Start a thread to asynchronously handle events associated with
	 * this link.
	 */
	void startThread();
	
	/**
	 * Converts a string state value to an integer value.
	 * 
	 * @param str string value.
	 * @return Integer value.
	 */
	int whatState(std::string str);
	

	Links *linkManager;
	
	
	// Information about the link that is written outside of the Link
	// thread but read/copied by the thread.
	Node* currentNode;
	Node* attachNode;
	bool canAttach;
	
	// Information about the link maintained by the thread. This may not
	// match the externally visible values since the thread (and the router,
	// for that matter) run asynchronously.
	int thrdState;
	Node* thrdCurrentNode; 
	int waitPolicy;
	bool metaDataSent;
	
	// Mechanism used to synchronize the thread making copies of the above values.
	std::queue <int> syncQ;
	sem_t synSem;

	XMLTree *contactAttrs;
	// State should be taken from the state variables rather then this element.
	XMLTree *linkAttrs;
	XMLTree *clInfo;
	std::string remoteAddr;

	bool openRequested;
	
	pthread_t thread;

	

	bool threadRun;

	const static int ACTION_NONE = 0;
	const static int ACTION_RUN  = 1;
	const static int ACTION_STOP = 2;

	// For telling the thread to stop.
	static int action;;

	// Messages are externally queued here to wake up the link thread.
	std::queue <LinkMessage*> msgQueue;
	sem_t queueSem;
	// Message used to wake up the link thread. The type is used
	// only for debugging; the thread does not really care why it
	// was woken up.
	
	/**
	 * Search for a "contact_attr" element as a direct child element. If
	 * found, save it. Also, look and see if it has a "link_attr" element
	 * as a child.
	 * 
	 * @param event Root event where the contact_attr is expected to be a
	 *    direct child.
	 */
	void findAndSaveAttributesContact(XMLTree *event);
		
	/**
	 * Search for a "link_attr" element as a direct child element. If
	 * found, save it. Also, look and see if it has a "clinfo" element
	 * or "remote_eid" as a child.
	 * 
	 * @param event Root event where the link_attr is expected to be a
	 *    direct child.
	 */
	void findAndSaveAttributesLink(XMLTree *event);
	
	/**
	 * Search for a "clinfo" element as a direct child element. If
	 * found, save it.
	 * 
	 * @param event Root event where the clinfo is expected to be a
	 *    direct child.
	 */
	void findAndSaveAttributesCLinfo(XMLTree *event);
	
	
	/**
	 * Search for a "remote_eid" element as a direct child element. If
	 * found, save the "uri" attribute.
	 * 
	 * @param event Root event where the remote_eid is expected to be a
	 *    direct child.
	 */
	void findAndSaveAttributesRemoteEID(XMLTree* event);
		
	/**
	 * Saves the contact_attr element.
	 * 
	 * @param element XML element to be saved.
	 */
	void storeContactElement(XMLTree *element) 
	{
		contactAttrs = element;
	}
	
	/**
	 * Saves the link_attr element.
	 * 
	 * @param element XML element to be saved.
	 */
	void storeLinkElement(XMLTree *element) 
	{
		linkAttrs = element;
	}
	
	/**
	 * Saves the clinfo element.
	 * 
	 * @param element XML element to be saved.
	 */
	void storeCLinfoElement(XMLTree *element) 
	{
		clInfo = element;
	}


};
#endif
